-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement HTTP Authentication provider and allow ApiKey
authentication by default.
#58126
Conversation
Pinging @elastic/kibana-security (Team:Security) |
ff60dbe
to
a531d9b
Compare
a531d9b
to
dd03c4f
Compare
: ({ username: 'awesome', full_name: 'Awesome D00d' } as AuthenticatedUser) | ||
); | ||
return authc; | ||
} | ||
|
||
export const authenticationMock = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: consumers are supposed to use mocks provided by the plugin itself and to not redefine it to be as much in sync with real types as possible.
throw error; | ||
} | ||
this.log.debug(`Attempting to authenticate a user`); | ||
const user = authentication!.getCurrentUser(request); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: getCurrentUser
is synchronous and doesn't throw errors anymore, but it can return null
.
['Basic xxx yyy', 'basic'], | ||
['basic xxx', 'basic'], | ||
['basic', 'basic'], | ||
// We don't trim leading whitespaces in scheme. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: we never trimmed so we're not changing anything here (not sure if Core HTTP service does this though).
@@ -4,9 +4,6 @@ | |||
* you may not use this file except in compliance with the Elastic License. | |||
*/ | |||
|
|||
import sinon from 'sinon'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: that's what caused 50%+ of the changes in this PR - I finally decided to drop support for sinon
in auth provider tests as more changes in auth providers are coming (Login Selector) and I don't want to increase technical debt.
|
||
const authenticationResult = await provider.login( | ||
httpServerMock.createKibanaRequest(), | ||
httpServerMock.createKibanaRequest({ headers: {} }), |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
}, | ||
{ | ||
validate(value) { | ||
if (value.providers.includes('http')) { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
if (hasProvider('basic') && hasProvider('token')) { | ||
log( | ||
'Enabling both `basic` and `token` authentication providers in `xpack.security.authc.providers` is deprecated. Login page will only use `token` provider.' |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -9,7 +9,7 @@ import expect from '@kbn/expect/expect.js'; | |||
import { FtrProviderContext } from '../../ftr_provider_context'; | |||
|
|||
export default function({ getService }: FtrProviderContext) { | |||
const supertest = getService('supertest'); | |||
const supertestWithoutAuth = getService('supertestWithoutAuth'); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
), | ||
http: schema.object({ | ||
enabled: schema.boolean({ defaultValue: true }), | ||
autoSchemesEnabled: schema.boolean({ defaultValue: true }), |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Hey @kobelb, this one is ready for the first review pass whenever you have time, thanks! |
ACK: reviewing first thing tomorrow, apologies for the delay |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just great! I really like the approach that you took here.
@@ -9,7 +9,7 @@ import expect from '@kbn/expect/expect.js'; | |||
import { FtrProviderContext } from '../../ftr_provider_context'; | |||
|
|||
export default function({ getService }: FtrProviderContext) { | |||
const supertest = getService('supertest'); | |||
const supertestWithoutAuth = getService('supertestWithoutAuth'); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
if (hasProvider('basic') && hasProvider('token')) { | ||
log( | ||
'Enabling both `basic` and `token` authentication providers in `xpack.security.authc.providers` is deprecated. Login page will only use `token` provider.' |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
), | ||
http: schema.object({ | ||
enabled: schema.boolean({ defaultValue: true }), | ||
autoSchemesEnabled: schema.boolean({ defaultValue: true }), |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
}, | ||
{ | ||
validate(value) { | ||
if (value.providers.includes('http')) { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
const authenticationResult = await provider.login( | ||
httpServerMock.createKibanaRequest(), | ||
httpServerMock.createKibanaRequest({ headers: {} }), |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
constructor( | ||
protected readonly options: Readonly<AuthenticationProviderOptions>, | ||
proxyOptions?: Readonly<Partial<HTTPAuthenticationProviderOptions>> |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); | ||
mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(new errors.ServiceUnavailable()); | ||
mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); | ||
|
||
const authenticationResult = await provider.authenticate(request, null); | ||
|
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -105,6 +106,7 @@ const providerMap = new Map< | |||
[TokenAuthenticationProvider.type, TokenAuthenticationProvider], | |||
[OIDCAuthenticationProvider.type, OIDCAuthenticationProvider], | |||
[PKIAuthenticationProvider.type, PKIAuthenticationProvider], | |||
[HTTPAuthenticationProvider.type, HTTPAuthenticationProvider], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we get some benefit I'm not realizing from making the HTTPAuthenticationProvider
part of the providerMap
? If it wasn't part of the providerMap
, could we get rid of the custom validate
function? Separately, I was wondering whether we could pass all of the enabled providers into the HTTPAuthenticationProvider
constructor, add a method to each provider to denote which auth header should be supported, and use this as the basis for isSchemeSupported
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we get some benefit I'm not realizing from making the HTTPAuthenticationProvider part of the providerMap?
Not really, was experimenting with different options and just stopped at some point.
If it wasn't part of the providerMap, could we get rid of the custom validate function?
Yep, that would simplify the config part.
Separately, I was wondering whether we could pass all of the enabled providers into the HTTPAuthenticationProvider constructor, add a method to each provider to denote which auth header should be supported, and use this as the basis for isSchemeSupported...
That's a great idea! But I'd propose slightly modified version - Authenticator
will calculate proper supportedSchemes
on its own and just give them to HTTPAuthenticationProvider
so that it doesn't have to deal with other providers or even know that they exist. I'll make that change and you'll tell if it looks good to you or not :)
… supported by the HTTP authentication provider.
@kobelb thanks for review! PR should be ready for another review pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The improvements to the tests and the changes to the HTTP authentication provider look great!!
7.x/7.7.0: 7fd27c3 |
Great to see this happening and having it enabled by default. @nchaulet We need to check in Fleet for the case this is disabled. |
💔 Build FailedTest FailuresKibana Pipeline / kibana-xpack-agent / Chrome X-Pack UI Functional Tests.x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job·ts.machine learning anomaly detection single metric job cloning creates the job and finishes processingStandard Out
Stack Trace
Kibana Pipeline / kibana-xpack-agent / Chrome X-Pack UI Functional Tests.x-pack/test/functional/apps/transform/creation_index_pattern·ts.transform creation_index_pattern batch transform with terms+date_histogram groups and avg agg adds the aggregation entriesStandard Out
Stack Trace
Kibana Pipeline / kibana-xpack-agent / Chrome X-Pack UI Functional Tests.x-pack/test/functional/apps/transform/creation_index_pattern·ts.transform creation_index_pattern batch transform with terms+date_histogram groups and avg agg adds the aggregation entriesStandard Out
Stack Trace
and 1 more failures, only showing the first 3. History
To update your PR or re-run it, just comment with: |
This PR moves HTTP authentication logic (the one that relies on HTTP
Authorization: Basic/Bearer/ApiKey
header) out of existing providers into a dedicated one. This newHTTP Authentication
provider is enabled by default and also supports two additional features out of the box (configured viakibana.yml
):It allows authentication of the requests with
Authorization
HTTP header with schemes used by other currently enabled authentication providers (basic
--->Authorization: Basic xxxx
,saml
--->Authorization: Bearer xxx
etc.). It's needed for the BWC reasons, we may want to turn this off once all dependent code switches to API keys for authentication.It allows authentication of the requests using Elasticsearch API keys within
Authorization
HTTP header (requirement for Authentication to Kibana using API Keys #56087)So if we expand default configuration it will look like this:
Also this new functionality can be used to allow requests to the Kibana APIs using
Authorization: Basic xxx
even ifbasic
authentication provider isn't enabled (requirement for #53910). Here is example config:The configuration above will allow user login only via SAML and additionally programmatic access to the Kibana APIs with authentication via
Authorization: ApiKey xxx
andAuthorization: Basic xxx
HTTP headers.Note to reviewers: a big chunk of the changes are related to the tests (refactored them to finally remove
sinon
dependency).Fixes: #56087, #53910
Prerequisite for: #53010
"Release Note: Kibana now allows authentication via Elasticsearch API keys by default."